<?php
/**
 * Created by PhpStorm.
 * User: lzh 9157374
 * Date: 2019/9/11
 * Time: 1:35 PM
 */
namespace Core\common;

class Wechat
{
    use \Core\Traits\Wechat;

    /**
     * @param string $url
     * @return mixed
     */
    public static function getJsSdk($url = '')
    {
        try{
            $account_api = \WeAccount::create();
            $sdk = $account_api->getJssdkConfig($url);

            return $sdk;
        }catch (\Exception $e){
            exit($e->getMessage());
        }
    }

    /**
     * 微信网页授权
     * @param string $scope
     * @param bool $refresh
     * @return mixed
     */
    public static function oauth2($scope = 'userinfo',$refresh = false)
    {
        $code = I('code');
        $url  = Request::url();
        $oauthType   = Assist::globalVal('oauth_account.type');
        $oauthAppId  = Assist::globalVal('oauth_account.key');
        $sessionOpenidKey = $oauthAppId.'__openid__';

        if($refresh === false){
            $openid = $_SESSION[$sessionOpenidKey];
            if($openid){
                $_userInfoKey = '__wechat_user_info_'.$openid.'__';
                $userInfo = Cache::get($_userInfoKey);
                if($userInfo){
                    $userInfo['_isCache'] = 1;
                    return $userInfo;
                }
            }
        }
        if(empty($code)){
            self::authRedirect($oauthAppId,$url,$scope,$oauthType);
        }

        $oauthAppSecret = Assist::globalVal('oauth_account.secret');
        $userInfo = self::oauth2UserInfo($oauthAppId,$oauthAppSecret,$code,$oauthType);
        $_SESSION[$sessionOpenidKey] = $userInfo['openid'];
        $_userInfoKey = '__wechat_user_info_'.$userInfo['openid'].'__';
        Cache::set($_userInfoKey,$userInfo,86400*7);

        return $userInfo;
    }

    /**
     * @Notes:  获取手机号
     * @Author   lzh
     */
    function WxGetMoble(){
        global $_GPC, $_W;
        $code = $_GPC['code'];
        $appid =  $_W['account']['key'];
        $secret = $_W['account']['secret'];
        $Url = "https://api.weixin.qq.com/sns/jscode2session?appid=".$appid."&secret=".$secret."&js_code=".$code."&grant_type=authorization_code";
        $tokenArr = ihttp_request($Url,"GET");
        $sessionKey = json_decode($tokenArr['content'], true);
        $sessionKey = $sessionKey['session_key'];
        $encryptedData= $_GPC['mobile'];
        $iv = $_GPC['iv'];
        $errCode = $this->wxtel($encryptedData, $iv, $data,$appid, $sessionKey);
        if ($errCode == 0) {
            $data = json_decode($errCode, true);
            $errno = 1;
            $message = '请求成功';
        } else {
            $data = $errCode;
            $errno = 2;
            $message = '手机号获取失败，请重试';
        }
        $return = [
            'data'=> $data,
            'errno'=> $errno,
            'message'=> $message,
        ];
        return $return;
    }

    //解密微信小程序获取手机号
    public function wxtel( $encryptedData, $iv, &$data, $appid, $sessionKey )
    {
        /*
        -41001: encodingAesKey 非法
        -41003: aes 解密失败
        -41004: 解密后得到的buffer非法
        -41005: base64加密失败
        -41016: base64解密失败
        */
        if (strlen($sessionKey) != 24) {
            return '-41001';
        }
        $aesKey=base64_decode($sessionKey);
        if (strlen($iv) != 24) {
            return '-41002';
        }
        $aesIV=base64_decode($iv);
        $aesCipher=base64_decode($encryptedData);
        $ree=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
        $dataObj=json_decode( $ree );
        if( $dataObj  == NULL )
        {
            return '-41003';
        }
        if( $dataObj->watermark->appid != $appid )
        {
            return '-41003';
        }
        $data = $ree;
        return $ree;
    }

    /**
     * @Notes:  支付
     * @Author   lzh
     * @DateTime 2019-09-16
     * @param    [type]     $aData      [openid  money  number]
     * @param    string     $notify_url [description]
     * @return   [type]                 [description]
     */
    function WxPay($aData,$url=''){
        global $_W;
        $appid   = $_W['account']['key']; //appid
        $mch_id  = $_W['account']['setting']['payment']['wechat']['mchid']; //商户号
        $key     = $_W['account']['setting']['payment']['wechat']['signkey']; //秘钥

        $openid = $aData['openid']; //openid
        $money = $aData['money'];//需要支付金额
        $orderCode = $aData['order_no'];//订单编号
        $notify_url = $_W['siteroot'] . 'addons/'.MOD_ADD.'/notify/'.$url;

        $str = str_shuffle($this->createNoncestr());
        $pay['appid'] = $appid;
        $pay['body'] = 'test';               //商品描述
        $pay['mch_id'] = $mch_id;            //商户号
        $pay['nonce_str'] =  $str;       //随机字符串
        $pay['notify_url'] = $notify_url;    //支付回调地址
        $pay['openid'] = $openid;
        $pay['out_trade_no'] = $orderCode;       //订单号
        $pay['spbill_create_ip'] = $_SERVER['SERVER_ADDR']; // 终端IP
        $pay['total_fee'] = 100*$money; //支付金额
        $pay['trade_type'] = 'JSAPI';    //交易类型
        //组建签名（不可换行 空格  否则哭吧）
        $stringA="appid=".$pay['appid']."&body=".$pay['body']."&mch_id=".$pay['mch_id']."&nonce_str=".$pay['nonce_str']."&notify_url=".$pay['notify_url']."&openid=". $pay['openid']."&out_trade_no=".$pay['out_trade_no']."&spbill_create_ip=". $pay['spbill_create_ip']."&total_fee=".$pay['total_fee']."&trade_type=".$pay['trade_type'];
        $stringSignTemp=$stringA."&key=".$key; //注：key为商户平台设置的密钥key(这个还需要再确认一下)
        $sign= strtoupper(md5($stringSignTemp)); //注：MD5签名方式
        $pay['sign'] = $sign;              //签名
        //统一下单请求
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $data = $this->arrayToXml($pay);
        //return $data;
        $res = $this->wxpost($url,$data);


        //对 统一下单返回得参数进行处理
        $pay_arr = $this->xmlToArray($res);  //这里是数组

        if ($pay_arr['return_code'] == 'FAIL' || $pay_arr['result_code'] == 'FAIL')
        {
            echo  json_encode($res);exit;
        }
        //调起支付数据签名字段
        $timeStamp = time();
        $nonce_pay = str_shuffle($str);
        $package = $pay_arr['prepay_id'];
        $signType = "MD5";
        $stringPay = "appId=".$appid."&nonceStr=".$nonce_pay."&package=prepay_id=".$package."&signType=".$signType."&timeStamp=".$timeStamp."&key=".$key;
        $paySign = strtoupper(md5($stringPay));
        $rpay['timeStamp'] = (string)$timeStamp;
        $rpay['nonceStr'] = $nonce_pay;
        $rpay['package'] = "prepay_id=".$package;
        $rpay['signType'] = $signType;
        $rpay['paySign'] = $paySign;
        $rpay['orders'] =  $orderCode ;  //订单流水号id    $order
        return   $rpay;
    }

    /**
     * [sendMoney 企业付款到零钱]
     * @param [type] $amount  [发送的金额（分）目前发送金额不能少于1元]
     * @param [type] $re_openid [发送人的 openid]
     * @param string $desc  [企业付款描述信息 (必填)]
     * @param string $trade_no [订单单号]
     * @return [type]    [description]
     */
    function sendMoney($re_openid,$amount,$trade_no,$desc='礼物回收'){
        global $_W;
        $appid      = $_W['account']['key']; //appid
        $mchid      = $_W['account']['setting']['payment']['wechat']['mchid']; //商户号
        $apikey     = $_W['account']['setting']['payment']['wechat']['signkey']; //秘钥

        $total_amount = (100) * $amount;

        $data=array(
            'mch_appid'=>$appid,//商户账号appid
            'mchid'=> $mchid,//商户号
            'nonce_str'=>$this->createNoncestr(),//随机字符串
            'partner_trade_no'=> empty($trade_no) ? time() . random(4, true) : $trade_no,//商户订单号
            'openid'=> $re_openid,//用户openid
            'check_name'=>'NO_CHECK',//校验用户姓名选项,
            'amount'=>$total_amount,//金额
            'desc'=> $desc,//企业付款描述信息
            'spbill_create_ip'=> gethostbyname($_SERVER["HTTP_HOST"]),//Ip地址
        );

        //生成签名算法
        $secrect_key=$apikey;///这个就是个API密码。MD5 32位。
        $data=array_filter($data);
        ksort($data);
        $str='';
        foreach($data as $k=>$v) {
            $str.=$k.'='.$v.'&';
        }
        $str.='key='.$secrect_key;
        $data['sign']=md5($str);
        //生成签名算法
        $xml=$this->arraytoxml($data);

        $url='https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'; //调用接口
        $res=$this->curl_post_ssl($url,$xml);
        $return = $this->xmltoarray($res);


        // $responseObj = simplexml_load_string($res, 'SimpleXMLElement', LIBXML_NOCDATA);
        // $res= $responseObj->return_code; //SUCCESS 如果返回来SUCCESS,则发生成功，处理自己的逻辑
        if($return['return_code']== SUCCESS){
            return $return;

        }else{
            return false;
        }

    }

    /**
     * [sendMoney 微信原路退回]
     * @param [type] $amount  [发送的金额（分）目前发送金额不能少于1元]
     * @param [type] $re_openid [发送人的 openid]
     */
    function WxRefund($data){
        global $_W;
        $appid      = $_W['account']['key']; //appid
        $mchid      = $_W['account']['setting']['payment']['wechat']['mchid']; //商户号
        $apikey     = $_W['account']['setting']['payment']['wechat']['signkey']; //秘钥


        $data=array(
            'mch_appid'=>$appid,//商户账号appid
            'mchid'=> $mchid,//商户号
            'nonce_str'=>$this->createNoncestr(),//随机字符串
            'out_trade_no'=> $data['out_trade_no'],//商户订单号
            'out_refund_no'=> $data['out_refund_no'],//商户退款单号
            'openid'=> $data['openid'],//用户openid
            'total_fee'=>(100) * $data['total_fee'],//订单金额
            'refund_fee'=>(100) * $data['refund_fee'],//退款金额
        );

        //生成签名算法
        $secrect_key=$apikey;///这个就是个API密码。MD5 32位。
        $data=array_filter($data);
        ksort($data);
        $str='';
        foreach($data as $k=>$v) {
            $str.=$k.'='.$v.'&';
        }
        $str.='key='.$secrect_key;
        $data['sign']=md5($str);
        //生成签名算法
        $xml=$this->arraytoxml($data);

        $url='https://api.mch.weixin.qq.com/secapi/pay/refund'; //调用接口
        $res=$this->curl_post_ssl($url,$xml);
        $return = $this->xmltoarray($res);

        if($return['return_code']== SUCCESS){
            return $return;

        }else{
            return false;
        }

    }

    /**
     * @Notes:  post 请求
     * @Author   lzh
     * @DateTime 2019-09-16
     * @param    [type]     $url  [description]
     * @param    [type]     $post [description]
     * @return   [type]           [description]
     */
    function wxpost($url,$post)
    {
        //初始化
        $curl = curl_init();
        $header[] = "Content-type: text/xml";//定义content-type为xml
        //设置抓取的url
        curl_setopt($curl, CURLOPT_URL, $url);
        //设置头文件的信息作为数据流输出
//        curl_setopt($curl, CURLOPT_HEADER, 1);
        //定义请求类型
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        //设置获取的信息以文件流的形式返回，而不是直接输出。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        //设置post方式提交
        curl_setopt($curl, CURLOPT_POST, 1);
        //设置post数据
        $post_data = $post;
        curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
        //执行命令
        $data = curl_exec($curl);
        //关闭URL请求
        //显示获得的数据
//        print_r($data);
        if ($data)
        {
            curl_close($curl);
            return $data;
        }else{
            $res = curl_error($curl);
            curl_close($curl);
            return $res;
        }
    }

    /**
     * [curl_post_ssl 发送curl_post数据]
     * @param [type] $url  [发送地址]
     * @param [type] $xmldata [发送文件格式]
     * @param [type] $second [设置执行最长秒数]
     * @param [type] $aHeader [设置头部]
     * @return [type]   [description]
     */
    private function curl_post_ssl($url, $xmldata, $second = 30, $aHeader = array()){
        $isdir =  MODULE_ROOT.'/Core/cert/';//证书位置;绝对路径

        $ch = curl_init();//初始化curl

        curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置执行最长秒数
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// 终止从服务端进行验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
        curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//证书位置
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
        curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//证书位置
        curl_setopt($ch, CURLOPT_CAINFO, 'PEM');
        curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem');
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);//设置头部
        }
        curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xmldata);//全部数据使用HTTP协议中的"POST"操作来发送

        $data = curl_exec($ch);//执行回话
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "call faild, errorCode:$error\n";
            curl_close($ch);
            return false;
        }
    }

    /**
     * @Notes:  将数组转化为xml数据格式
     * @Author   lzh
     * @DateTime 2019-09-16
     * @param    [type]     $arr [description]
     * @return   [type]          [description]
     */
    function arrayToXml($arr){
        $xml = "<xml>";
        foreach ($arr as $key=>$val){
            if(is_array($val)){
                $xml.="<".$key.">".$this->arrayToXml($val)."</".$key.">";
            }else{
                $xml.="<".$key.">".$val."</".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }

    /**
     * @Notes:  将XML转化为json/数组
     * @Author   lzh
     * @DateTime 2019-09-16
     * @param    [type]     $xml  [description]
     * @param    string     $type [description]
     * @return   [type]           [description]
     */
    function xmlToArray($xml,$type='')
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        //simplexml_load_string()解析读取xml数据，然后转成json格式
        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if ($type == "json") {
            $json = json_encode($xmlstring);
            return $json;
        }
        $arr = json_decode(json_encode($xmlstring), true);
        return $arr;
    }

    /**
     * [createNoncestr 生成随机字符串]
     * @param integer $length [长度]
     * @return [type]   [字母大小写加数字]
     */
    private function createNoncestr($length =32){
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";

        for($i=0;$i<$length;$i++){
            $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }
}